package org.vectrics.common.util;


import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.Hashtable;
import org.vectrics.SystemException;
import org.vectrics.domain.Domain;
import org.w3c.dom.Document;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import org.xml.sax.*;
import javax.xml.parsers.*;
import org.apache.log4j.Category;



public abstract class ConfigurationReader
{
    private static Category log = Category.getInstance(ConfigurationReader.class);

    Document document = null;
    private String fileName = null;
   // Constants -----------------------------------------------------
    static Hashtable primitives = new Hashtable();


	protected abstract void load(Document doc) throws Exception;


    static
    {
        primitives.put("int", Integer.TYPE);
        primitives.put("boolean", Boolean.TYPE);
        primitives.put("double", Double.TYPE);
        primitives.put("float", Float.TYPE);
        primitives.put("long", Long.TYPE);
    }


    public Document parse(InputStream inputStream) {
		StringBuffer input = new StringBuffer();
		boolean done = false;
		try {
			while (!done) {
				int c = (int)inputStream.read();
				if (c < 0) {
					done = true;
				} else {
					input.append((char)c);
				}
			}
		} catch (IOException ioe) {
			throw new RuntimeException("IOException getting input", ioe);
		}
    	String contents = input.toString();
    	return(this.parse(contents));
    };
    
    public Document parse(String contents) {
    	if (contents == null) 
    		throw new SystemException("parse() was passed null contents");
        if (document == null) {
            this.parseContent(contents);
        }
        return(document);
    };

    public String getFileName()
    {
        return(fileName);
    };


    public void setFileName(String iFileName)
    {
        fileName = iFileName;
    };


    public String getName()
    {
       return "Configuration";
    }




   public String save()
        throws Exception
   {

        Writer out = new StringWriter();
        out.close();

        // Return configuration
        return out.toString();
   }



   public void saveConfiguration()
      throws Exception
   {
        // Get XML
        String xml = save();


        URL confFile = Thread.currentThread().getContextClassLoader().getResource(getFileName());

        if (confFile != null) {
            PrintWriter out = null;
            try {
         	    out = new PrintWriter(new FileOutputStream(confFile.getFile()));
    	    } catch (java.io.FileNotFoundException e) {
	     	    log.error("Configuration file " + confFile.getFile()
                        + " must be available and writable.", e);
	        }
            out.print(xml);
	        out.close();
        }
    }



    //------------------------------------------------------------------------------------
/*
    public boolean loadFromFile(String configPath)
       throws IOException, Exception {
        boolean success = false;
        if (fileName == null)
            throw new IOException("Call setFileName() before using configuration reader.");

        String pathAndFile = configPath;
        //if (pathAndFile.endsWith("/") == false)
        //    pathAndFile += "/";
        //pathAndFile += getFileName();
        log.info("loadPath() - opening file: " + pathAndFile);
        FileReader reader = null;
        try {
            reader = new FileReader(pathAndFile);
            success = true;
        }
        catch (Exception ex) {
            log.error("loadPath() - exception opening file: " + pathAndFile, ex);
        }

        if (reader == null) {
            throw new IOException("File not found: " + getFileName());
        }
        else {
            try {
                StringBuffer arr = new StringBuffer();
                int c;
                while ((c = reader.read()) >= 0) {
                    arr.append((char)c);
                }
                parseContent(arr.toString());
            }
            catch (Exception ex) {
                log.error("Exception reading/parsing file: " + pathAndFile, ex);
                throw new IOException("Exception reading/parsing file: " + pathAndFile);
            }
        }
        return(success);
    }

    //------------------------------------------------------------------------------------
    public boolean loadFromServletFile(String fileName)
    {
        boolean success = false;
        try {
            if (fileName == null)
                throw new IOException("Call setFileName() before using configuration reader.");

           // Load user config from XML

           //log.info("File Test attempting to find file: " + getFileName());
           //File file = new File(getFileName());
           //if (file == null)
           //     throw new IOException("File not found: " + getFileName());
           //file.setReadOnly();
           //log.info("File Test found file.");

           InputStream conf = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream(getFileName());

            if (conf == null) {
                log.info("Could not open file: " + getFileName());
                throw new Exception("Could not open file: " + getFileName());
            }
            byte[] arr = new byte[conf.available()];
            conf.read(arr);
            conf.close();
            success = true;

            String cfg = new String(arr);
            parseContent(cfg);
        }
        catch (Exception ex) {
            success = false;
            log.info("Exception occurred trying to load configuration file from classpath.  File = "
                    + getFileName() + "  Exception: " + ex.getMessage());
        }
        return(success);
    }
*/

    protected void parseContent(String content) {
       // Parse XML
       try {
           DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
           DocumentBuilder parser = factory.newDocumentBuilder();


           try {
               log.debug("Config xml = " + content);
               document = parser.parse(new InputSource(new StringReader(content)));

               load(document);

               //File file = new File(getFileName());
               //if (file == null)
               //     throw new IOException("File not found: " + getFileName());
               //file.setReadOnly();
               //document = parser.parse(file);
               //file = null;
           }

           catch (SAXException se)
           {
                throw new SystemException("SAX Exception parsing content", se);
           }

        }
        catch (javax.xml.parsers.ParserConfigurationException pe) {
            throw new SystemException("Parser exception during config read. " + pe.getMessage(), pe);
        }
        catch (Exception ex) {
            log.error("Exception parsing XML content: \r\n" + content, ex);
            throw new SystemException("Excpeption parsing config file. " , ex);
        }
    }

    private boolean isAttributeWriteable(String className, String attribute, String type) {
        Class arg = null;
        Class cls = null;
        try {
            if(type.equals("int"))
                arg = Integer.TYPE;
            else if(type.equals("boolean"))
                arg = Boolean.TYPE;
            else if(type.equals("float"))
                arg = Float.TYPE;
            else if(type.equals("byte"))
                arg = Byte.TYPE;
            else if(type.equals("short"))
                arg = Short.TYPE;
            else if(type.equals("char"))
                arg = Character.TYPE;
            else if(type.equals("long"))
                arg = Long.TYPE;
            else if(type.equals("double"))
                arg = Double.TYPE;
            else arg = Class.forName(type);
        } catch(ClassNotFoundException e) {
            log.error("Unable to check parameter of type '"+type+"'", e);
            return false;
        }
        try {
            cls = Class.forName(className);
        } catch(ClassNotFoundException e) {
            log.error("Unable to check MBean of type '" + className + "'", e);
            return false;
        }
        try {
            Method m = cls.getMethod("set"+attribute, new Class[]{arg});
            return m != null && Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers()) && m.getReturnType().equals(Void.TYPE);
        } catch(NoSuchMethodException e) {}
        return false;
    }
}


